/*
 * Main module
 *
 * Copyright (C) 2008 Sinitsin Ivan (Etersoft) <ivan@etersoft.ru>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#include <string.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
//#include "winuser.h"
#include "winerror.h"

#include <windows.h> 

#include "ole2.h"

//#include "dll.h"
#include "ai-lib-common.h"

#include "common/debug.h"

HINSTANCE AiLib_hInstance = 0;
LONG dll_ref = 0;
BOOL write_log = 1;
//LONG OOVersion = VER_3;


/******************************************************************************
 * AddIn ClassFactory
 */
typedef struct {
    IClassFactory ITF_IClassFactory;
    LONG ref;
    HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
} IClassFactoryImpl;

struct object_creation_info
{
    const CLSID *clsid;
    HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
};

static const struct object_creation_info object_creation[] =
{
    { &CLSID_AiSQL, SQL_Create }
////    { &CLSID_AMDirectDrawStream, AM_create },
////    { &CLSID_MediaStreamFilter, MediaStreamFilter_create }
};

static HRESULT WINAPI
AiLib_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj)
{
    IClassFactoryImpl *This = (IClassFactoryImpl *)iface;

    if (IsEqualGUID(riid, &IID_IUnknown)
	|| IsEqualGUID(riid, &IID_IClassFactory))
    {
	IClassFactory_AddRef(iface);
	*ppobj = This;
	return S_OK;
    }

//    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
    return E_NOINTERFACE;
}

static ULONG WINAPI AiLib_AddRef(LPCLASSFACTORY iface)
{
//MessageBox(0,"BEGIN","AiLib_AddRef",MB_OK);
    IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
    return InterlockedIncrement(&This->ref);
}

static ULONG WINAPI AiLib_Release(LPCLASSFACTORY iface)
{
//MessageBox(0,"BEGIN","AiLib_Release",MB_OK);
    IClassFactoryImpl *This = (IClassFactoryImpl *)iface;

    ULONG ref = InterlockedDecrement(&This->ref);

    if (ref == 0)
	HeapFree(GetProcessHeap(), 0, This);

    return ref;
}

static HRESULT WINAPI AiLib_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pOuter,
					  REFIID riid, LPVOID *ppobj)
{
    IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
    HRESULT hres;
    LPUNKNOWN punk;

    *ppobj = NULL;
    hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk);
    
    if (SUCCEEDED(hres)) {
        hres = IUnknown_QueryInterface(punk, riid, ppobj);
        IUnknown_Release(punk);
    }
    return hres;
}

static HRESULT WINAPI AiLib_LockServer(LPCLASSFACTORY iface,BOOL dolock)
{
MessageBox(0,"BEGIN","AiLib_LockServer",MB_OK);
    IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
//    FIXME("(%p)->(%d),stub!\n",This,dolock);
    return S_OK;
} 

static const IClassFactoryVtbl DSCF_Vtbl =
{
    AiLib_QueryInterface,
    AiLib_AddRef,
    AiLib_Release,
    AiLib_CreateInstance,
    AiLib_LockServer
};

/*******************************************************************************/


BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
{
    SHOW("%p 0x%x %p\n", hinstDLL, fdwReason, fImpLoad);

    switch(fdwReason) {
    case DLL_PROCESS_ATTACH:
        //DisableThreadLibraryCalls(hinstDLL);
        AiLib_hInstance = hinstDLL;
        //init_session(TRUE);
	break;

    case DLL_PROCESS_DETACH:
//        if (hCabinet)
//            FreeLibrary(hCabinet);
//        hCabinet = NULL;
//        init_session(FALSE);
        AiLib_hInstance = 0;
	break;
    }
    return TRUE;
}

/*******************************************************************************
 * DllGetClassObject [AMSTREAM.@]
 * Retrieves class object from a DLL object
 *
 * NOTES
 *    Docs say returns STDAPI
 *
 * PARAMS
 *    rclsid [I] CLSID for the class object
 *    riid   [I] Reference to identifier of interface for class object
 *    ppv    [O] Address of variable to receive interface pointer for riid
 *
 * RETURNS
 *    Success: S_OK
 *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
 *             E_UNEXPECTED
 */
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{

    WCHAR str_clsid[39];
    WCHAR str_riid[39];
    StringFromGUID2(rclsid, str_clsid, 39);
    StringFromGUID2(riid, str_riid, 39);
 SHOW("(%s,%s,%p)\n", str_clsid, str_riid, ppv);
    unsigned int i;
    IClassFactoryImpl *factory;
    
    char file_name[]= {'\\','a','i','l','i','b','.','l','o','g',0};
    int len=0;
    
    //TRACE_IN;
//    len = GetSystemDirectory(buf, MAX_PATH);
//    if (len) {
//        while (file_name[i]!=0) {buf[len+i]=file_name[i];i++;};
//        //if (GetFileAttributesA(buf) != 0xFFFFFFFF) {
//            write_log = 1;
//            trace_file = fopen(buf,"a");
//            if (trace_file) fclose(trace_file);
//        //}
//    }
    
//    WCHAR str_clsid[39];
//    WCHAR str_riid[39];
//    StringFromGUID2(rclsid, str_clsid, 39);
//    StringFromGUID2(riid, str_riid, 39);
//    MessageBoxW(0, str_clsid, str_riid, MB_OK); 
//    
//    StringFromGUID2(&IID_IClassFactory, str_clsid, 39);
//    StringFromGUID2(&IID_IUnknown, str_riid, 39);
//    
//        MessageBoxW(0, str_clsid, str_riid, MB_OK);
//    TRACE_IN;
    //WTRACE(L"rclsid = (%s) riid = (%s) \n", str_clsid, str_riid);
    
//    if ( !IsEqualGUID( &IID_IClassFactory, rclsid )
//	 && ! IsEqualGUID( &IID_IUnknown, rclsid) )
//	return E_NOINTERFACE;

    for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
    {
    	if (IsEqualGUID(object_creation[i].clsid, rclsid))
    	    break;
    }

    if (i == sizeof(object_creation)/sizeof(object_creation[0]))
    {
    	return CLASS_E_CLASSNOTAVAILABLE;
    }
    
    factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory));
    if (factory == NULL) return E_OUTOFMEMORY;

    factory->ITF_IClassFactory.lpVtbl = (IClassFactoryVtbl*)&DSCF_Vtbl;
    factory->ref = 1;
    
    factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
    
    *ppv = &(factory->ITF_IClassFactory);
//    MessageBox(0,"*ppv = &(factory->ITF_IClassFactory);","DllGetClassObject",MB_OK);
    return S_OK;
}

STDAPI DllCanUnloadNow(void)
{
//    TRACE("GLOBAL REF = %i \n",dll_ref);
    return dll_ref != 0 ? S_FALSE : S_OK;
}
